{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/nceder/qpb4e/blob/main/code/Chapter%2017/Chapter_17.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 17 Data types as objects"
      ],
      "metadata": {
        "id": "DtyhVMydZqLT"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 17.1 Types are objects, too"
      ],
      "metadata": {
        "id": "qbo-UASVZuRD"
      }
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "02s0Oda5ZCV4",
        "outputId": "2441b5e1-25a1-43d9-eb8c-8fc1d7b92330",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "type(5)"
      ],
      "execution_count": 1,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "int"
            ]
          },
          "metadata": {},
          "execution_count": 1
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "HhNE4wLrZCV9",
        "outputId": "72a4c95d-c0b3-415f-bac8-debe8e85e939",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "type(['hello', 'goodbye'])"
      ],
      "execution_count": 2,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "list"
            ]
          },
          "metadata": {},
          "execution_count": 2
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "4QomHdQJZCWB",
        "outputId": "85079dec-aa0a-44d3-b00c-6b90fdb231ae",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "type_result = type(5)\n",
        "type(type_result)"
      ],
      "execution_count": 3,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "type"
            ]
          },
          "metadata": {},
          "execution_count": 3
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 17.2 Using types"
      ],
      "metadata": {
        "id": "Lhfs2nAxaBAx"
      }
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "rUza_usLZCWC",
        "outputId": "6dddaa1b-ef91-4448-fd34-165cde9adcce",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "type(\"Hello\") == type(\"Goodbye\")"
      ],
      "execution_count": 4,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "metadata": {},
          "execution_count": 4
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "A0B2El77ZCWD",
        "outputId": "73b47b3a-3a03-4c9d-87d0-538907892d29",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "type(\"Hello\") == type(5)"
      ],
      "execution_count": 5,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "False"
            ]
          },
          "metadata": {},
          "execution_count": 5
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 17.3 Types and user-defined classes"
      ],
      "metadata": {
        "id": "B7jnB7sZafjD"
      }
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Cf4l2fb3ZCWD"
      },
      "source": [
        "class A:\n",
        "    pass\n",
        "\n",
        "class B(A):\n",
        "    pass\n"
      ],
      "execution_count": 6,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "wDBdW8EFZCWJ",
        "outputId": "91ae52d6-8d1e-492b-cff1-4db7bafe3148",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "b = B()\n",
        "type(b)"
      ],
      "execution_count": 7,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "__main__.B"
            ]
          },
          "metadata": {},
          "execution_count": 7
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "4ympjMW9ZCWK",
        "outputId": "0a093171-9a0a-47ae-b4f1-c944877e111e",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "b.__class__"
      ],
      "execution_count": 8,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "__main__.B"
            ]
          },
          "metadata": {},
          "execution_count": 8
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "i2O-0k7nZCWL",
        "outputId": "cebbc4c4-5a21-4ce1-e89f-82daf6ed5eb7",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "b_class = b.__class__\n",
        "b_class == B"
      ],
      "execution_count": 9,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "metadata": {},
          "execution_count": 9
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "7g8cFIQaZCWO",
        "outputId": "1e78d8c8-8a25-4b8d-e568-a1dfb65728f8",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        }
      },
      "source": [
        "b_class.__name__"
      ],
      "execution_count": 10,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'B'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 10
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "l5fmQKvpZCWP",
        "outputId": "d09c6341-5787-45b4-d9ed-01b0089fded3",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "b_class.__bases__"
      ],
      "execution_count": 11,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "(__main__.A,)"
            ]
          },
          "metadata": {},
          "execution_count": 11
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "VSjkbtuUZCWQ",
        "outputId": "e2025374-5ebe-4dbc-f497-5213d54fdd1c",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "class C:\n",
        "    pass\n",
        "\n",
        "class D:\n",
        "    pass\n",
        "\n",
        "class E(D):\n",
        "    pass\n",
        "\n",
        "x = 12\n",
        "c = C()\n",
        "d = D()\n",
        "e = E()\n",
        "isinstance(x, E)"
      ],
      "execution_count": 12,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "False"
            ]
          },
          "metadata": {},
          "execution_count": 12
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "st8h4CvXZCWR",
        "outputId": "1b9180eb-832a-4a81-e3c8-fbf17c743a6a",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "isinstance(c, E)             #A"
      ],
      "execution_count": 13,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "False"
            ]
          },
          "metadata": {},
          "execution_count": 13
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "WFLGxVuRZCWS",
        "outputId": "e2e2d4f5-b66e-4b9e-8f13-d3bcd69c9b46",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "isinstance(e, E)"
      ],
      "execution_count": 14,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "metadata": {},
          "execution_count": 14
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "XsMGGptxZCWT",
        "outputId": "aee59475-3f6c-4f4b-c447-6a70acff0813",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "isinstance(e, D)             #B"
      ],
      "execution_count": 15,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "metadata": {},
          "execution_count": 15
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "A9hMr9uqZCWT",
        "outputId": "b2f7ecae-a648-467f-f5c3-287811e608ea",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "isinstance(d, E)                #C"
      ],
      "execution_count": 16,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "False"
            ]
          },
          "metadata": {},
          "execution_count": 16
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "7DZxlt5eZCWU",
        "outputId": "ff100eba-ac0c-473a-b54c-8b2b942ce84e",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "y = 12\n",
        "isinstance(y, type(5))             #D"
      ],
      "execution_count": 17,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "metadata": {},
          "execution_count": 17
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "X7tsVWnKZCWX",
        "outputId": "c46f75f9-6001-46c4-ae73-1a93d18d3670",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "issubclass(C, D)"
      ],
      "execution_count": 18,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "False"
            ]
          },
          "metadata": {},
          "execution_count": 18
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "jkgYoZm0ZCWY",
        "outputId": "64a51ce2-7d2e-4ad8-f0f4-7164dfe7f221",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "issubclass(E, D)"
      ],
      "execution_count": 19,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "metadata": {},
          "execution_count": 19
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "bxoxkRffZCWY",
        "outputId": "d5dd5f8c-dc5a-4abe-d78b-05918a3bb9b4",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "issubclass(D, D)           #E"
      ],
      "execution_count": 20,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "metadata": {},
          "execution_count": 20
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "FvAKpQoHZCWY",
        "outputId": "5c153d8d-3ac8-4bfe-c425-77d15d6d9335",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "issubclass(e.__class__, D)"
      ],
      "execution_count": 21,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "metadata": {},
          "execution_count": 21
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Quick Check: Types\n",
        "Suppose that you want to make sure that object `x` is a `list` before you try appending to it. What code would you use? What would be the difference between using `type()` and `isinstance()`? Would this be the look before you leap (LBYL) or easier to ask forgiveness than permission (EAFP) of programming? What other options might you have besides checking the type explicitly?"
      ],
      "metadata": {
        "id": "uuXuqSLJYCe0"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "#### Solution\n",
        "```python\n",
        "x = []\n",
        "if isinstance(x, list):\n",
        "    print(\"is list\")\n",
        "    ```\n",
        "Using type would get only lists, not anything that subclasses lists. Either way, it's LBYL programming.\n",
        "You might also wrap the append in a try... except block and catch TypeError exceptions, which would be more EAFP.\n"
      ],
      "metadata": {
        "id": "9JPJloHmYQMl"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 17.5 What is a special method attribute?"
      ],
      "metadata": {
        "id": "14hmijhHaqNp"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Listing 17.1 File color_module.py"
      ],
      "metadata": {
        "id": "zWzhvXeQatdv"
      }
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "x5ayVBN4ZCWZ"
      },
      "source": [
        "# Listing 17.1 File color_module.py\n",
        "\n",
        "class Color:\n",
        "    def __init__(self, red, green, blue):\n",
        "        self._red = red\n",
        "        self._green = green\n",
        "        self._blue = blue\n",
        "    def __str__(self):\n",
        "        return f\"Color: R={self._red:d}, G={self._green:d}, B={self._blue:d}\""
      ],
      "execution_count": 22,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "SZI8UtNeZCWa"
      },
      "source": [
        "#from color_module import Color\n",
        "c = Color(15, 35, 3)"
      ],
      "execution_count": 23,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "VD2FAJ8lZCWc",
        "outputId": "8bf2c587-128a-43ba-f21f-e7fdf7739126",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "print(c)"
      ],
      "execution_count": 24,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Color: R=15, G=35, B=3\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 17.7 The `__getitem__` special method attribute"
      ],
      "metadata": {
        "id": "1SEC9NMKa3qG"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Quick Check: __getitem__ The example use of __getitem__ above is very limited and won’t work correctly in many situations. What are some cases in which the implementation above will fail or work incorrectly?\n",
        "\n",
        "#### Solution\n",
        "This implementation will not work if you try to access an item directly by index; neither can you move backward."
      ],
      "metadata": {
        "id": "Iafnk_YzYgwi"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class LineReader:\n",
        "    def __init__(self, filename):\n",
        "        self.fileobject = open(filename, 'r')              #A\n",
        "    def __getitem__(self, index):\n",
        "        line = self.fileobject.readline()                  #B\n",
        "        if line == \"\":                                #C\n",
        "            self.fileobject.close()      #D\n",
        "            raise IndexError         #E\n",
        "\n",
        "        else:\n",
        "            return line.split(\"::\")[:2]                    #F\n"
      ],
      "metadata": {
        "id": "5lGIk9RybSuv"
      },
      "execution_count": 25,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 17.8 Giving an object full list capability"
      ],
      "metadata": {
        "id": "2bA10U1MbZdw"
      }
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "v72QEH4HZCWc"
      },
      "source": [
        "class TypedList:\n",
        "    def __init__(self, example_element, initial_list=[]):\n",
        "        self.type = type(example_element)                     #A\n",
        "        if not isinstance(initial_list, list):\n",
        "            raise TypeError(\"Second argument of TypedList must \"\n",
        "                          \"be a list.\")\n",
        "        for element in initial_list:\n",
        "                if not isinstance(element, self.type):\n",
        "                    raise TypeError(\"Attempted to add an element of \"\n",
        "                                  \"incorrect type to a typed list.\")\n",
        "        self.elements = initial_list[:]"
      ],
      "execution_count": 26,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "JTUjXTl6ZCWd"
      },
      "source": [
        "class TypedList:\n",
        "    def __init__(self, example_element, initial_list=[]):\n",
        "        self.type = type(example_element)\n",
        "        if not isinstance(initial_list, list):\n",
        "            raise TypeError(\"Second argument of TypedList must \"\n",
        "                            \"be a list.\")\n",
        "        for element in initial_list:\n",
        "            self.__check(element)\n",
        "        self.elements = initial_list[:]\n",
        "    def __check(self, element):\n",
        "        if type(element) != self.type:\n",
        "            raise TypeError(\"Attempted to add an element of \"\n",
        "                            \"incorrect type to a typed list.\")\n",
        "    def __setitem__(self, i, element):\n",
        "        self.__check(element)\n",
        "        self.elements[i] = element\n",
        "    def __getitem__(self, i):\n",
        "        return self.elements[i]"
      ],
      "execution_count": 27,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "LiZoKb6lZCWe",
        "outputId": "9e549435-e78f-4b70-bad5-54513b7ed362",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "x = TypedList(\"\", 5 * [\"\"])\n",
        "x[2] = \"Hello\"\n",
        "x[3] = \"There\"\n",
        "print(x[2] + ' ' + x[3])"
      ],
      "execution_count": 28,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Hello There\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "bNCiKR14ZCWf",
        "outputId": "34b1ca90-0684-4d25-a516-5ba7990b7896",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "a, b, c, d, e = x\n",
        "a, b, c, d"
      ],
      "execution_count": 29,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "('', '', 'Hello', 'There')"
            ]
          },
          "metadata": {},
          "execution_count": 29
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Try This: Implementing list special methods\n",
        "Try implementing the `__len__` and `__delitem__` special methods for TypedList, as well as an append method."
      ],
      "metadata": {
        "id": "Etc2--iuXI-8"
      }
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "tfzbXaw1XQH7"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# @title\n",
        "class TypedList:\n",
        "    def __init__(self, example_element, initial_list=[]):\n",
        "        self.type = type(example_element)\n",
        "        if not isinstance(initial_list, list):\n",
        "            raise TypeError(\"Second argument of TypedList must \"\n",
        "                            \"be a list.\")\n",
        "        for element in initial_list:\n",
        "            self.__check(element)\n",
        "        self.elements = initial_list[:]\n",
        "    def __check(self, element):\n",
        "        if type(element) != self.type:\n",
        "            raise TypeError(\"Attempted to add an element of \"\n",
        "                            \"incorrect type to a typed list.\")\n",
        "    def __setitem__(self, i, element):\n",
        "        self.__check(element)\n",
        "        self.elements[i] = element\n",
        "    def __getitem__(self, i):\n",
        "        return self.elements[i]\n",
        "\n",
        "    ################################\n",
        "    ## SOLUTION\n",
        "    ## added methods\n",
        "    def __delitem__(self, i):\n",
        "        del self.elements[i]\n",
        "    def __len__(self):\n",
        "        return len(self.elements)\n",
        "    def append(self, element):\n",
        "        self.__check(element)\n",
        "        self.elements.append(element)\n",
        "\n",
        "x = TypedList(1, [1,2,3])\n",
        "print(len(x))\n",
        "x.append(1)\n",
        "del x[2]\n"
      ],
      "metadata": {
        "cellView": "form",
        "id": "jrRLN4CXXQTV"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## 17.9.1 Subclassing list"
      ],
      "metadata": {
        "id": "23CoGTIgbjot"
      }
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "NjKaEHORZCWg"
      },
      "source": [
        "class TypedListList(list):\n",
        "    def __init__(self, example_element, initial_list=[]):\n",
        "        self.type = type(example_element)\n",
        "        if not isinstance(initial_list, list):\n",
        "            raise TypeError(\"Second argument of TypedList must \"\n",
        "                            \"be a list.\")\n",
        "        for element in initial_list:\n",
        "            self.__check(element)\n",
        "        super().__init__(initial_list)\n",
        "\n",
        "    def __check(self, element):\n",
        "        if type(element) != self.type:\n",
        "            raise TypeError(\"Attempted to add an element of \"\n",
        "                            \"incorrect type to a typed list.\")\n",
        "\n",
        "    def __setitem__(self, i, element):\n",
        "        self.__check(element)\n",
        "        super().__setitem__(i, element)"
      ],
      "execution_count": 30,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ivIfaTavZCWh",
        "outputId": "f976375e-6119-4bfe-9c93-2072be54f3b2",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "x = TypedListList(\"\", 5 * [\"\"])\n",
        "x[2] = \"Hello\"\n",
        "x[3] = \"There\"\n",
        "print(x[2] + ' ' + x[3])"
      ],
      "execution_count": 31,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Hello There\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "uYy5FrOJZCWh",
        "outputId": "b4d5a030-13c7-4b7f-cf06-753bf4b9b03a",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "a, b, c, d, e = x\n",
        "a, b, c, d"
      ],
      "execution_count": 32,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "('', '', 'Hello', 'There')"
            ]
          },
          "metadata": {},
          "execution_count": 32
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "CbrdEi8kZCWi",
        "outputId": "854aa331-f88c-42d5-f5fa-f05a91b59544",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "x[:]"
      ],
      "execution_count": 33,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "['', '', 'Hello', 'There', '']"
            ]
          },
          "metadata": {},
          "execution_count": 33
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "hQaT-pr0ZCWj",
        "outputId": "76e88849-54b1-4680-b572-99902bc5c478",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "del x[2]\n",
        "x[:]"
      ],
      "execution_count": 34,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "['', '', 'There', '']"
            ]
          },
          "metadata": {},
          "execution_count": 34
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "GKRPScrzZCWj",
        "outputId": "51274b53-2e2e-4dd9-a509-c7d586428032",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "x.sort()\n",
        "x[:]"
      ],
      "execution_count": 35,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "['', '', '', 'There']"
            ]
          },
          "metadata": {},
          "execution_count": 35
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## 17.9.2 Subclassing UserList"
      ],
      "metadata": {
        "id": "PMHdAjZtb0OF"
      }
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "K92YwdpGZCWk"
      },
      "source": [
        "from collections import UserList\n",
        "class TypedUserList(UserList):\n",
        "    def __init__(self, example_element, initial_list=[]):\n",
        "        self.type = type(example_element)\n",
        "        if not isinstance(initial_list, list):\n",
        "            raise TypeError(\"Second argument of TypedList must \"\n",
        "                            \"be a list.\")\n",
        "        for element in initial_list:\n",
        "            self.__check(element)\n",
        "        super().__init__(initial_list)\n",
        "\n",
        "    def __check(self, element):\n",
        "        if type(element) != self.type:\n",
        "            raise TypeError(\"Attempted to add an element of \"\n",
        "                            \"incorrect type to a typed list.\")\n",
        "    def __setitem__(self, i, element):\n",
        "        self.__check(element)\n",
        "        self.data[i] = element\n",
        "    def __getitem__(self, i):\n",
        "        return self.data[i]"
      ],
      "execution_count": 36,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "_m8knXi3ZCWl",
        "outputId": "4262f76d-ce1b-4ab0-d8ab-5c34db6d7c11",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "x = TypedUserList(\"\", 5 * [\"\"])\n",
        "x[2] = \"Hello\"\n",
        "x[3] = \"There\"\n",
        "print(x[2] + ' ' + x[3])"
      ],
      "execution_count": 37,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Hello There\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "gj2wLRj3ZCWl",
        "outputId": "ba19e80b-76a1-406b-8c78-c6dc3e64f030",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "a, b, c, d, e = x\n",
        "a, b, c, d"
      ],
      "execution_count": 38,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "('', '', 'Hello', 'There')"
            ]
          },
          "metadata": {},
          "execution_count": 38
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "vDCFSO66ZCWm",
        "outputId": "1eabe94f-a544-4142-bbf0-297b20031342",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "x[:]"
      ],
      "execution_count": 39,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "['', '', 'Hello', 'There', '']"
            ]
          },
          "metadata": {},
          "execution_count": 39
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "jSafP6u1ZCWm",
        "outputId": "e5060454-1020-4163-9dd9-bd34aabc85e4",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "del x[2]\n",
        "x[:]"
      ],
      "execution_count": 40,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "['', '', 'There', '']"
            ]
          },
          "metadata": {},
          "execution_count": 40
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "cZMkerJMZCWp",
        "outputId": "7479e59e-0478-4e41-e9ae-34599e822fcb",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "x.sort()\n",
        "x[:]"
      ],
      "execution_count": 41,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "['', '', '', 'There']"
            ]
          },
          "metadata": {},
          "execution_count": 41
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Quick Check: Special method attributes and subclassing  types\n",
        "\n",
        "Suppose that you want a dictionary-like type that allows only strings as keys (maybe to make it work like a shelf object, as described in chapter 13). What options would you have for creating such a class? What would be the advantages and disadvantages of each option?"
      ],
      "metadata": {
        "id": "4WBkIZ83Ysbq"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "#### Solution\n",
        "\n",
        "You could use the same approach as you did for `TypedList` and inherit from the `UserDict` class. You could also inherit directly from `dict`, or you could implement all of the `dict` functionality yourself.\n",
        "\n",
        "Implementing everything yourself provides the most control but is the most work and most prone to bugs. If the changes you need to make are small (in this case, just checking the type before adding a key), it might make the most sense to inherit directly from `dict`. On the other hand, inheriting from `UserDict` is probably safest, because the internal `dict` object will continue to be a regular `dict`, which is a highly optimized and mature implementation."
      ],
      "metadata": {
        "id": "14zJGWKIYztA"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Lab 17: Creating a string only key:value dictionary\n",
        "\n",
        "The quick check above mentions creating a dictionary that only allows strings as keys. Let's that idea a step further and actually implement a dictionary that only allows strings for both the keys and values. This sort of dictionary might be useful for example to cache URL's and web pages in a web application.\n",
        "\n",
        "As mentioned in discussing lists above, you would have three possible approaches - write a class from scratch, inherit from the built-in dictionary, or inherit from UserDictionary. I would suggest for the best combination of simplicity and functionality that you inherit from the built-in `dict` type and override the  `__setitem__()` method.  "
      ],
      "metadata": {
        "id": "pYdQ40fHxGzz"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\" Create a dictionary that allows only strings for keys and values\"\"\"\n",
        "\n",
        "class StringDict(dict):\n",
        "    def __setitem__(self, key, value):\n",
        "        if not isinstance(key, str):\n",
        "            raise TypeError('keys must be strings') #A\n",
        "        if not isinstance(value, str):\n",
        "            raise TypeError('values must be strings')\n",
        "        super().__setitem__(key, value)\n",
        "\n",
        "    def __init__(self, *args, **kwargs):\n",
        "        super().__init__(*args, **kwargs)\n",
        "        key_error = any(not isinstance(_, str) for _ in self.keys()) #B\n",
        "        if key_error:\n",
        "            raise TypeError('keys must be strings')\n",
        "        value_error = any(not isinstance(_, str) for _ in self.values())\n",
        "        if value_error:\n",
        "            raise TypeError('values must be strings')"
      ],
      "metadata": {
        "id": "Id7yeRk8m6Zc"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Tests\n",
        "\n",
        "These can be run after executing each cell which declares StringDict class."
      ],
      "metadata": {
        "id": "dbvxOb00ggV2"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "test_dict = StringDict()\n",
        "test_dict['a'] = 'b'\n",
        "test_dict['c'] = 'd'\n",
        "print(test_dict)  # Output: {'a': 'b', 'c': 'd'}\n",
        "\n",
        "test_dict = StringDict(a='b', c='d')\n",
        "print(test_dict)  # Output: {'a': 'b', 'c': 'd'}\n",
        "\n",
        "test_dict = StringDict([('a', 'b'), ('c', 'd')])\n",
        "print(test_dict)  # Output: {'a': 'b', 'c': 'd'}\n"
      ],
      "metadata": {
        "id": "-XM2ONOHxaWF",
        "outputId": "5a4b3812-6bdf-4ac8-d156-ee7cf3692a75",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "{'a': 'b', 'c': 'd'}\n",
            "{'a': 'b', 'c': 'd'}\n",
            "{'a': 'b', 'c': 'd'}\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "test_dict = StringDict()\n",
        "test_dict['a'] = 1  # Raises TypeError: values must be strings\n",
        "test_dict[2] = 'd'  # Raises TypeError: keys must be strings\n",
        "\n",
        "test_dict = StringDict({a:1})  # Raises TypeError: values must be strings\n",
        "test_dict = StringDict({2:'d'})  # Raises TypeError: keys must be strings\n",
        "\n",
        "test_dict = StringDict([(1, 'b'), ('c', 'd')])  # Raises TypeError: keys must be strings\n",
        "test_dict = StringDict([('a', 1), ('c', 'd')])  # Raises TypeError: values must be strings"
      ],
      "metadata": {
        "id": "aIajfXcsxhMF",
        "outputId": "a11bd666-0a35-4444-9ef9-a04cb24271af",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 332
        }
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "error",
          "ename": "TypeError",
          "evalue": "values must be strings",
          "traceback": [
            "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
            "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
            "\u001b[0;32m<ipython-input-5-64b03c47df5b>\u001b[0m in \u001b[0;36m<cell line: 2>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mtest_dict\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mStringDict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mtest_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'a'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m  \u001b[0;31m# Raises TypeError: values must be strings\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0mtest_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'd'\u001b[0m  \u001b[0;31m# Raises TypeError: keys must be strings\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0mtest_dict\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mStringDict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# Raises TypeError: values must be strings\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m<ipython-input-2-1d44de563963>\u001b[0m in \u001b[0;36m__setitem__\u001b[0;34m(self, key, value)\u001b[0m\n\u001b[1;32m      6\u001b[0m             \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'keys must be strings'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m#A\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      7\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m             \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'values must be strings'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      9\u001b[0m         \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__setitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     10\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;31mTypeError\u001b[0m: values must be strings"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Copilot solution\n",
        "\n",
        "Prompt:\n",
        "\n",
        "```Create a subclass of dictionary that allows only strings as both keys and values.```"
      ],
      "metadata": {
        "id": "dAoVa2b3e8QD"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class StringDict(dict):\n",
        "    def __setitem__(self, key, value):\n",
        "        if not isinstance(key, str):\n",
        "            raise TypeError('keys must be strings')\n",
        "        if not isinstance(value, str):\n",
        "            raise TypeError('values must be strings')\n",
        "        super().__setitem__(key, value)\n",
        "\n",
        "    def __init__(self, *args, **kwargs):\n",
        "        super().__init__(*args, **kwargs)\n",
        "        for key, value in self.items():\n",
        "            if not isinstance(key, str):\n",
        "                raise TypeError('keys must be strings')\n",
        "            if not isinstance(value, str):\n",
        "                raise TypeError('values must be strings')"
      ],
      "metadata": {
        "id": "DVxOotv21DYO"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Colaboratory solution\n",
        "\n",
        "Prompt 1:\n",
        "\n",
        "```Create a subclass of dictionary that allows only strings as both keys and values.```"
      ],
      "metadata": {
        "id": "FBOsSNkEfHdU"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class StringDict(dict):\n",
        "  def __setitem__(self, key, value):\n",
        "    if not isinstance(key, str) or not isinstance(value, str):\n",
        "      raise TypeError(\"Both keys and values must be strings.\")\n",
        "    super().__setitem__(key, value)"
      ],
      "metadata": {
        "id": "sBNrDPGOfOrk"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Prompt 1:\n",
        "\n",
        "```Create a subclass of dictionary that allows only strings as both keys and values, including when creating the instance.```"
      ],
      "metadata": {
        "id": "T9eI-Jg1fS5c"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class StringDict(dict):\n",
        "  def __init__(self, *args, **kwargs):\n",
        "    super().__init__()\n",
        "    for key, value in dict(*args, **kwargs).items():   #A\n",
        "      self[key] = value      #B\n",
        "\n",
        "  def __setitem__(self, key, value):\n",
        "    if not isinstance(key, str) or not isinstance(value, str):\n",
        "      raise TypeError(\"Both keys and values must be strings.\")\n",
        "    super().__setitem__(key, value)"
      ],
      "metadata": {
        "id": "y7Aw1M9hfZx_"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}